﻿#pragma once

#include "Misc/StdHead.h"
#include "Math/Math.h"
#include "Math/Coordinate.h"
#include "Misc/Name.h"

#include "Object/Object.h"
#include "SceneNodeComponent.h"
#include "SceneNode.m.h"

class RScene;
class RSceneNodeComponent;
class RTransformComponent;

RCLASS(ExtraHeader=["Scene.h", "TransformComponent.h"])
class WHENGINE_API RSceneNode: public RObject
{
    ROBJECT


protected:
    RFIELD()
    TSharedObjectPtr<RTransformComponent> Transform;

public:    
    virtual void FirstTimeInitializeObject() override;

    ~RSceneNode(){};

public:
    RFUNCTION()
    TSharedObjectPtr<RTransformComponent> GetTransform() const;

    RFUNCTION()
    SVec3 GetForwardDirection() const;

    RFUNCTION()
    SVec3 GetUpDirection() const;

    RFUNCTION()
    SVec3 GetRightDirection() const;

    RFUNCTION()
    SVec3 GetAxisDirection(EAxis InAxis) const;

    RFUNCTION()
    void SetWorldPosition(const SVec3& InPosition);

    RFUNCTION()
    SVec3 GetWorldPosition();
    
    RFUNCTION()
    void SetWorldRotation(const SQuat& InRotation);

    RFUNCTION()
    SQuat GetWorldRotation();

    RFUNCTION()
    SVec3 GetEulerAngleInDegree() const;

    RFUNCTION()
    void SetEulerAngleInDegree(const SVec3& InEulerAngleInDegree);

    SMatrix4x4 GetWorldTransform();

    RFUNCTION()
    void SetScale(const SVec3& InScale);

    RFUNCTION()
    SVec3 GetScale();


    //auto invoked when scence begin play
    //is sence is not set, this function will not be work
    void BeginPlay();
    
    //invoked in BeginPlay()
    RFUNCTION()
    virtual void OnBeginPlay() {};

    //if BeginPlay() invoked before, this function will be invoked when:
    //node removed from scene, or scene end play
    //this function is designed for node that need to release resource
    void EndPlay();

    RFUNCTION()
    bool IsPlaying()
    {
        return bPlaying;
    }
    
    //invoked in EndPlay()
    RFUNCTION()
    virtual void OnEndPlay() {};

    void Tick(float DeltaSeconds);
    
    void Paint(CRenderScene* InRenderSceneData2); 

    RFUNCTION()
    virtual void OnTick(float DeltaSeconds) {};

    RFUNCTION()
    void AddSubNode(RSceneNodePtr InSubNode,bool bKeepWorldPosRotationScale);
    
    RFUNCTION()
    void RemoveSubNode(RSceneNodePtr InSubNode);

    //remove this node from parent node
    RFUNCTION()
    void RemoveFromParent();

    //没有父节点
    RFUNCTION()
    bool IsRootNode() const;
    //是否有子节点
    RFUNCTION()
    bool HasSubNode() const;

    RFUNCTION()
    int64_t GetChildrenNum() const;

    RFUNCTION()
    TSharedObjectPtr<RSceneNode> GetChild(int64_t Index) const;

    template<typename NodeComponentType> 
    TSharedObjectPtr<NodeComponentType> CreateAndAddComponent()
    {
        return TSharedObjectPtr<NodeComponentType>(CreateAndAddComponent(NodeComponentType::StaticClass()));
    }

    RFUNCTION() 
    TSharedObjectPtr<RSceneNodeComponent> CreateAndAddComponent(
        const TSubclassOf<RSceneNodeComponent>& InComponentClass);

    RFUNCTION()
    void AddComponent(TSharedObjectPtr<RSceneNodeComponent> InComponent);

    RFUNCTION()
    void RemoveComponent(TSharedObjectPtr<RSceneNodeComponent> InComponent);

    RFUNCTION()
    void RemoveAllComponents();

    //get first component with InComponentClass
    TSharedObjectPtr<RSceneNodeComponent> GetComponent(CClass* InComponentClass);

    //get first component with InComponentClass
    RFUNCTION()
    TSharedObjectPtr<RSceneNodeComponent> GetComponent(
        const TSubclassOf<RSceneNodeComponent>& InComponentClass);

    template<typename ComponentType>
    TSharedObjectPtr<ComponentType> GetComponent()
    {
        return TSharedObjectPtr<ComponentType>(GetComponent(ComponentType::StaticClass()));
    }

    //get all components with InComponentClass
    TVector<TSharedObjectPtr<RSceneNodeComponent>> GetComponents(CClass* InComponentClass);

    //get all components with InComponentClass
    RFUNCTION()
    TVector<TSharedObjectPtr<RSceneNodeComponent>> GetComponents(
        const TSubclassOf<RSceneNodeComponent>& InComponentClass);


    template<typename ComponentType> 
    TVector<TSharedObjectPtr<ComponentType>> GetComponents()
    {
        auto Founded=GetComponents(ComponentType::StaticClass());
        TVector<TSharedObjectPtr<ComponentType>> Ret;
        Ret.Reserve(Founded.Num());
        for(auto& Component:Founded)
        {
            Ret.Add(Component); 
        }
        return Ret;

    }

    const TVector<TSharedObjectPtr<RSceneNodeComponent>>& GetAllComponents()
    {
        return Components;
    }
protected:    
    RFIELD()
    TVector<TSharedObjectPtr<RSceneNodeComponent>> Components;

public:

    RFUNCTION()
    TSharedObjectPtr<RScene> GetScene();

protected:
    bool bPlaying=false;
public:
    SMatrix4x4 GetViewMatrix();
};